#region File Description
//-----------------------------------------------------------------------------
// Screen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;
#endregion

namespace TimeWar
{
    /// <summary>
    /// Screen represents a unit of rendering for the game, generally transitional point such 
    /// as splash screens, selection screens and the actual game levels.
    /// </summary>
    abstract public class Screen
    {
        /// <summary>
        /// The root of the scene graph for this screen
        /// </summary>
        protected SceneItem scene = null;

        /// <summary>
        /// Overlay points to a screen that will be drawn AFTER this one, more than likely overlaying it
        /// </summary>
        protected Screen overlay;

        protected SpriteBatch batch = null;

        protected Game game = null;

        protected List<AI_PlayableObject> onscreenObjects = null;
        protected List<Weapon> onscreenWeapons = null;

        protected IOctree octree = null;

        protected Camera gameCamera = null;
        protected ChaseCamera chaseCamera = null;
        protected AI_PlayableObject chasedObject = null;
        protected CameraTypes cameraType = CameraTypes.MapView;

        protected int zoneCount = 0;

        public Game GameInstance
        {
            get
            {
                return game;
            }
        }

        public SpriteBatch SpriteBatch
        {
            get
            {
                return batch;
            }
        }

        public List<Weapon> OnscreenWeapons
        {
            get
            {
                return onscreenWeapons;
            }
        }

        public Screen(Game game)
        {
            this.game = game;
            this.scene = new SceneItem(game);

            if (game != null)
            {
                IGraphicsDeviceService graphicsService = (IGraphicsDeviceService)game.Services.GetService(typeof(IGraphicsDeviceService));
                batch = new SpriteBatch(graphicsService.GraphicsDevice);
            }

            onscreenObjects = new List<AI_PlayableObject>();
            onscreenWeapons = new List<Weapon>();
            octree = new Octree();

            gameCamera = new Camera();

            gameCamera.CameraPosition = new Vector3(0.0f, 5.0f, 5.0f);

            chaseCamera = new ChaseCamera();

            // Set the camera offsets
            chaseCamera.DesiredPositionOffset = new Vector3(0.0f, 1f, 6f);
            chaseCamera.LookAtOffset = new Vector3(0.0f, 150.0f, 0.0f);

            // Set camera perspective
            chaseCamera.NearPlaneDistance = 0.2f;
            chaseCamera.FarPlaneDistance = 500.0f;
        }

        protected void setupCameras()
        {

            chasedObject = getChasedObject();
            
            chaseCamera.ChaseDirection = chasedObject.Direction;

            chaseCamera.ChasePosition = chasedObject.Position;
            chaseCamera.ObjRotation = chasedObject.Rotation;
            chaseCamera.Up = chasedObject.Up;
            chaseCamera.setInitialValues();

            gameCamera.Update(chasedObject.Position, chasedObject.Rotation);

        }

        protected void callObjectUpdate(TimeSpan time, TimeSpan elapsedTime)
        {
            if (null == octree)
            {
                return;
            }

            List<VectorShape> objects = octree.GetNodes(Vector3.Zero, 1000);

            if (objects.Count < 1)
            {
                return;
            }

            foreach (VectorShape obj in objects)
            {
                obj.Update(time, elapsedTime);
            }


        }

        /// <summary>
        /// Update changes the layout and positions based on input or other variables
        /// Base class updates all items in the scene then calls any overlays to get them to render themselves
        /// </summary>
        /// <param name="time">Total game time since it was started</param>
        /// <param name="elapsedTime">Elapsed game time since the last call to update</param>
        /// <returns>The next gamestate to transition to. Default is the return value of an overlay or NONE. Override Update if you want to change this behaviour</returns>
        public virtual GameState Update(TimeSpan time, TimeSpan elapsedTime)
        {
            //Update the Scene
            scene.Update(time, elapsedTime);

            if (null == onscreenObjects || onscreenObjects.Count < 1)
            {
                //not a playable screen
                return GameState.None;
            }

            if (null == chasedObject)
            {
                return GameState.None;
            }

            //Update objects within scene/screen
            callObjectUpdate(time, elapsedTime);

            gameCamera.Update(chasedObject.Position, chasedObject.Rotation);
            chaseCamera.ChasePosition = chasedObject.Position;
            chaseCamera.Up = chasedObject.Up;
            chaseCamera.ObjRotation = chasedObject.Rotation;

            chaseCamera.Update(TimeSpan.FromSeconds(1));

            //process camera views:
            if (XInputHelper.GamePads[PlayerIndex.One].APressed)
            {
                if (CameraTypes.MapView == cameraType)
                {
                    cameraType = CameraTypes.ChaseView;
                }
                else if (CameraTypes.ChaseView == cameraType)
                {
                    cameraType = CameraTypes.MapView;
                }

            }

            checkForCollisions();

            //reset controllers
            resetControllers();

            //Default is no state changes, override the class if you want a different state
            return (overlay == null) ? GameState.None : overlay.Update(time, elapsedTime);
        }

        protected void resetControllers()
        {
            XInputHelper.GamePads[PlayerIndex.One].Update(game, Keyboard.GetState(PlayerIndex.One));
            XInputHelper.GamePads[PlayerIndex.Two].Update(game, Keyboard.GetState(PlayerIndex.Two));
            XInputHelper.GamePads[PlayerIndex.Three].Update(game, Keyboard.GetState(PlayerIndex.Three));
            XInputHelper.GamePads[PlayerIndex.Four].Update(game, Keyboard.GetState(PlayerIndex.Four));
        }

        protected void renderObjectsViaList()
        {
            if (null == onscreenObjects)
            {
                return;
            }
            if (onscreenObjects.Count < 1)
            {
                return;
            }

            Matrix cameraView = gameCamera.View;
            Matrix cameraProjection = gameCamera.Projection;

            if (cameraType == CameraTypes.MapView)
            {
                cameraView = gameCamera.View;
                cameraProjection = gameCamera.Projection;
            }
            else if (cameraType == CameraTypes.ChaseView)
            {
                cameraView = chaseCamera.View;
                cameraProjection = chaseCamera.Projection;
            }

            foreach (AI_PlayableObject obj in onscreenObjects)
            {
                obj.View = cameraView;
                obj.Projection = cameraProjection;
                obj.Render();
            }

            if (null == onscreenWeapons)
            {
                return;
            }
            if (onscreenWeapons.Count < 1)
            {
                return;
            }
            foreach (Weapon obj in onscreenWeapons)
            {
                obj.View = cameraView;
                obj.Projection = cameraProjection;
                obj.Render();
            }

        }

        protected void renderObjectsViaOctree()
        {
            if (null == octree)
            {
                return;
            }

            List<VectorShape> objects = null;
            objects = octree.GetNodes(Vector3.Zero, 1000);

            if (objects.Count < 1)
            {
                return;
            }

            Matrix cameraView = gameCamera.View;
            Matrix cameraProjection = gameCamera.Projection;

            if (cameraType == CameraTypes.MapView)
            {
                cameraView = gameCamera.View;
                cameraProjection = gameCamera.Projection;
            }
            else if (cameraType == CameraTypes.ChaseView)
            {
                cameraView = chaseCamera.View;
                cameraProjection = chaseCamera.Projection;
            }

            foreach (VectorShape obj in objects)
            {
                obj.View = cameraView;
                obj.Projection = cameraProjection;
                obj.Render();
            }

        }

        protected void objectRender()
        {
            //renderObjectsViaList();
            renderObjectsViaOctree();
        }


        /// <summary>
        /// Renders this scene. The base class renders everything in the sceen graph and then calls any overlays to 
        /// get them to render themselves
        /// </summary>
        public virtual void Render()
        {
            //Render this scene then any overlays
            scene.Render();

            objectRender();

            if (overlay != null)
                overlay.Render();
        }

        /// <summary>
        /// Tidies up the scene. Base class does nothing but calls the overlays 
        /// </summary>
        public virtual void Shutdown()
        {
            if (overlay != null)
                overlay.Shutdown();

            if (batch != null)
            {
                batch.Dispose();
                batch = null;
            }
        }

        /// <summary>
        /// OnCreateDevice is called when the device is created
        /// </summary>
        public virtual void OnCreateDevice()
        {
            //Re-Create the Sprite Batch!
            IGraphicsDeviceService graphicsService = (IGraphicsDeviceService)game.Services.GetService(typeof(IGraphicsDeviceService));
            batch = new SpriteBatch(graphicsService.GraphicsDevice);
        }

        protected AI_PlayableObject getChasedObject()
        {
            if (null == onscreenObjects)
            {
                return null;
            }

            if (onscreenObjects.Count < 1)
            {
                return null;
            }

            foreach (AI_PlayableObject obj in onscreenObjects)
            {
                if (true == obj.Chased)
                {
                    return obj;
                }
            }

            return null;
        }

        //Determine object collisions
        protected void orgCheckForCollisions()
        {
            if (null == onscreenObjects)
            {
                return;
            }

            if (onscreenObjects.Count < 1)
            {
                return;
            }

            for (int i = 0; i < onscreenObjects.Count - 1; i++)
            {
                AI_PlayableObject objA = onscreenObjects[i];

                for (int j = i + 1; j < onscreenObjects.Count; j++)
                {
                    AI_PlayableObject objB = onscreenObjects[j];

                    //check for collision and process
                    bool collided = objA.hasCollidedWith(objB);

                    if (collided)
                    {
                        //Process collision
                        processCollision(objA, objB);
                        objA.Move(TimeSpan.FromSeconds(10));
                        objB.Move(TimeSpan.FromSeconds(10));
                    }
                }

            }
        }

        protected void octreeCheckForCollisions(Vector3 screenPosition)
        {
            //Only checks if objects near the player object has collided
            List<VectorShape> screenItems = octree.GetNodes(screenPosition, 1000);

            if (null == screenItems || screenItems.Count < 2)
            {
                return;
            }

            for (int i = 0; i < screenItems.Count - 1; i++)
            {
                VectorShape objA = screenItems[i];

                for (int j = i + 1; j < screenItems.Count; j++)
                {
                    VectorShape objB = screenItems[j];

                    //check for collision and process
                    bool collided = objA.hasCollidedWith(objB);

                    ShapeTypes typeA = objA.ShapeType;
                    ShapeTypes typeB = objB.ShapeType;

                    if (typeA == ShapeTypes.Character || typeB == ShapeTypes.Character)
                    {
                        //check
                        //typeB = typeB;
                        if (typeA == ShapeTypes.Weapon)
                        {
                            String x = game.ToString();
                        }
                        else if (typeB == ShapeTypes.Weapon)
                        {
                            String x = game.ToString();
                        }
                    }

                    if (collided)
                    {
                        Cue currAudioCue = null;
                        

                        if (objA.ShapeType == ShapeTypes.Weapon)
                        {
                            if (objB.ShapeType == ShapeTypes.Starship)
                            {
                                if (((Weapon)objA).Player == ((Starship)objB).Player)
                                {
                                    //friendly fire?
                                    currAudioCue = Sound.Play(Sounds.CountDownWarning);
                                }
                                else
                                {
                                    //game.Exit();
                                    currAudioCue = Sound.Play(Sounds.Exterminate);
                                }
                            }
                            else if (objB.ShapeType == ShapeTypes.Character)
                            {
                                

                                //game.Exit();  Game crashed here Oct 12, 2008
                                //Therefore has correctly determined impact of weapon against character
                                ((AI_PlayableObject)objB).sustainDamage(((Weapon)objA).Damage);


                            }
                        }
                        else if (objB.ShapeType == ShapeTypes.Weapon)
                        {
                            if (objA.ShapeType == ShapeTypes.Starship)
                            {
                                if (((Weapon)objB).Player == ((Starship)objA).Player)
                                {
                                    //friendly fire?
                                    currAudioCue = Sound.Play(Sounds.ExplodeShip);
                                }
                                else
                                {
                                    //game.Exit();
                                    currAudioCue = Sound.Play(Sounds.HyperspaceActivate);
                                }
                            }
                            else
                            {
                                //game.Exit();
                                currAudioCue = Sound.Play(Sounds.RocketFire);
                            }

                        }
                        else
                        {

                            //Process collision
                            processCollision((AI_PlayableObject)objA, (AI_PlayableObject)objB);
                            objA.Move(TimeSpan.FromSeconds(10));
                            objB.Move(TimeSpan.FromSeconds(10));

                        }

                    }
                }

            }


        }

        public void checkForCollisions()
        {
            //orgCheckForCollisions();
            //int totalObjectsInScreen = 0;

            Vector3 screenSector;

            if (zoneCount > 3)
            {
                zoneCount = 0;
            }

            if (zoneCount == 0)
            {
                screenSector = Vector3.Zero;
            }
            else if (zoneCount == 1)
            {
                screenSector = new Vector3(1000f, 0, 0);
            }
            else if (zoneCount == 2)
            {
                screenSector = new Vector3(0, 1000f, 0);
            }
            else
            {
                screenSector = new Vector3(0, 0, 1000f);
            }

            octreeCheckForCollisions(screenSector);

            zoneCount++;

        }

        protected void processCollision(AI_PlayableObject objA, AI_PlayableObject objB)
        {
            objB.Velocity = objA.getAfterCollisionVelocity(objB);
        }

        public void weaponDischarged(Weapon x)
        {
            addScreenObject(x);
        }

        public void addScreenObject(AI_PlayableObject obj)
        {
            octree.AddNode(obj.Position, obj);
            onscreenObjects.Add(obj);
        }

        public void addScreenObject(Weapon obj)
        {
            octree.AddNode(obj.Position, obj);
            onscreenWeapons.Add(obj);
        }

    }
}
